
from datetime import timedelta
import Signals
import pandas as pd

from Evaluate import equity_curve_for_OKEx_USDT_future_next_open_kaka, equity_curve_for_OKEx_USDT_future_next_open_ori, equity_curve_for_OKEx_USDT_future_next_open_kaka2
from Function import root_path
from Position import position_for_OKEx_future
from Statistics import transfer_equity_curve_to_trade_kaka, transfer_equity_curve_to_trade, strategy_evaluate

pd.set_option('expand_frame_repr', False)  # 当列太多时不换行
pd.set_option('display.max_rows', 300)  # 最多显示数据的行数

# =====手工设定策略参数
symbol_face_value = {'BTC': 0.01, 'EOS': 10, 'ETH': 0.1, 'LTC': 1,  'XRP': 100, 'DOGE': 1000}
min_margin_ratio = 1 / 100  # 最低保证金率，低于就会爆仓
drop_days = 0  # 币种刚刚上线10天内不交易


# 执行回测处理
def run_backtest(signal_name='signal_simple_bolling', para=[200,2], rule_type='15T', date_from='2021-01-01', date_to='2021-12-08', symbol='ETH', leverage_rate=1, slippage=0.001, c_rate=0.0005):

    if_kaka = 1
    if signal_name in ('signal_simple_bolling', 'signal_v3_atr'):
        if_kaka = 0
    elif signal_name == 'signal_reverse_trade':
        if_kaka = 2

    # =====读入数据
    df = pd.read_pickle(root_path + '/data/spot/%s-USDT_15m_spot.pkl' % symbol)
    # 任何原始数据读入都进行一下排序、去重，以防万一
    df.sort_values(by=['candle_begin_time'], inplace=True)
    df.drop_duplicates(subset=['candle_begin_time'], inplace=True)
    df.reset_index(inplace=True, drop=True)
    # =====转换为其他分钟数据
    period_df = df.resample(rule=rule_type, on='candle_begin_time', label='left', closed='left').agg(
        {'open': 'first',
         'high': 'max',
         'low': 'min',
         'close': 'last',
         'volume': 'sum',
         'quote_volume': 'sum',
         'trade_num': 'sum',
         'taker_buy_base_asset_volume': 'sum',
         'taker_buy_quote_asset_volume': 'sum',
         })
    period_df.dropna(subset=['open'], inplace=True)  # 去除一天都没有交易的周期
    period_df = period_df[period_df['volume'] > 0]  # 去除成交量为0的交易周期
    period_df.reset_index(inplace=True)
    df = period_df[['candle_begin_time', 'open', 'high', 'low', 'close', 'volume', 'quote_volume', 'trade_num',
                    'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume']]
    # 时间段筛选
    if date_from != '':
        df = df[df['candle_begin_time'] >= pd.to_datetime(date_from)]
    if date_to != '':
        df = df[df['candle_begin_time'] <= pd.to_datetime(date_to)]

    df.reset_index(inplace=True, drop=True)

    # =====计算交易信号
    df = getattr(Signals, signal_name)(df, para=para)

    # =====计算实际持仓
    df = position_for_OKEx_future(df, signal_name)

    # =====计算资金曲线
    # 选取相关时间。币种上线10天之后的日期
    t = df.iloc[0]['candle_begin_time'] + timedelta(days=drop_days)
    df = df[df['candle_begin_time'] > t]

    # 计算资金曲线
    face_value = symbol_face_value[symbol.split('-')[0]]

    if if_kaka == 1:
        # 动态止损时第二个滑点设置为0
        df = equity_curve_for_OKEx_USDT_future_next_open_kaka(df, slippage=slippage, slippage2=0, c_rate=c_rate,
                                                              leverage_rate=leverage_rate,
                                                              face_value=face_value, min_margin_ratio=min_margin_ratio)

        # =====策略评价
        # 计算每笔交易
        trade = transfer_equity_curve_to_trade_kaka(df)

    elif if_kaka == 2:
        df = equity_curve_for_OKEx_USDT_future_next_open_kaka2(df, slippage=slippage, c_rate=c_rate,
                                                             leverage_rate=leverage_rate,
                                                             face_value=face_value, min_margin_ratio=min_margin_ratio)
        trade = transfer_equity_curve_to_trade_kaka(df)

    else:
        df = equity_curve_for_OKEx_USDT_future_next_open_ori(df, slippage=slippage, c_rate=c_rate,
                                                             leverage_rate=leverage_rate,
                                                             face_value=face_value, min_margin_ratio=min_margin_ratio)
        trade = transfer_equity_curve_to_trade(df)

    # 计算各类统计指标
    r, monthly_return = strategy_evaluate(df, trade)

    return r, df, trade


def run_backtest_klines(rule_type='15T', date_from='2021-01-01', date_to='2021-12-08', symbol='ETH'):

    # =====读入数据
    df = pd.read_pickle(root_path + '/data/spot/%s-USDT_15m_spot.pkl' % symbol)
    # 任何原始数据读入都进行一下排序、去重，以防万一
    df.sort_values(by=['candle_begin_time'], inplace=True)
    df.drop_duplicates(subset=['candle_begin_time'], inplace=True)
    df.reset_index(inplace=True, drop=True)
    # =====转换为其他分钟数据
    period_df = df.resample(rule=rule_type, on='candle_begin_time', label='left', closed='left').agg(
        {'open': 'first',
         'high': 'max',
         'low': 'min',
         'close': 'last',
         'volume': 'sum',
         'quote_volume': 'sum',
         'trade_num': 'sum',
         'taker_buy_base_asset_volume': 'sum',
         'taker_buy_quote_asset_volume': 'sum',
         })
    period_df.dropna(subset=['open'], inplace=True)  # 去除一天都没有交易的周期
    period_df = period_df[period_df['volume'] > 0]  # 去除成交量为0的交易周期
    period_df.reset_index(inplace=True)
    df = period_df[['candle_begin_time', 'open', 'high', 'low', 'close', 'volume', 'quote_volume', 'trade_num',
                    'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume']]
    # 时间段筛选
    if date_from != '':
        df = df[df['candle_begin_time'] >= pd.to_datetime(date_from)]
    if date_to != '':
        df = df[df['candle_begin_time'] <= pd.to_datetime(date_to)]

    df.reset_index(inplace=True, drop=True)

    return df


def run_backtest_trade(signal_name='signal_reverse_trade', para=[200,2], rule_type='15T', date_from='2021-01-01', date_to='2021-12-08', symbol='ETH', leverage_rate=1, slippage=0.001, c_rate=0.0005):

    if_kaka = 1
    if signal_name in ('signal_simple_bolling', 'signal_v3_atr'):
        if_kaka = 0
    elif signal_name == 'signal_reverse_trade':
        if_kaka = 2

    # =====读入数据
    df = pd.read_pickle(root_path + '/data/spot/%s-USDT_15m_spot.pkl' % symbol)
    # 任何原始数据读入都进行一下排序、去重，以防万一
    df.sort_values(by=['candle_begin_time'], inplace=True)
    df.drop_duplicates(subset=['candle_begin_time'], inplace=True)
    df.reset_index(inplace=True, drop=True)
    # =====转换为其他分钟数据
    period_df = df.resample(rule=rule_type, on='candle_begin_time', label='left', closed='left').agg(
        {'open': 'first',
         'high': 'max',
         'low': 'min',
         'close': 'last',
         'volume': 'sum',
         'quote_volume': 'sum',
         'trade_num': 'sum',
         'taker_buy_base_asset_volume': 'sum',
         'taker_buy_quote_asset_volume': 'sum',
         })
    period_df.dropna(subset=['open'], inplace=True)  # 去除一天都没有交易的周期
    period_df = period_df[period_df['volume'] > 0]  # 去除成交量为0的交易周期
    period_df.reset_index(inplace=True)
    df = period_df[['candle_begin_time', 'open', 'high', 'low', 'close', 'volume', 'quote_volume', 'trade_num',
                    'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume']]
    # 时间段筛选
    if date_from != '':
        df = df[df['candle_begin_time'] >= pd.to_datetime(date_from)]
    if date_to != '':
        df = df[df['candle_begin_time'] <= pd.to_datetime(date_to)]

    df.reset_index(inplace=True, drop=True)

    # =====计算交易信号
    df = getattr(Signals, signal_name)(df, para=para)

    # =====计算实际持仓
    df = position_for_OKEx_future(df, signal_name)

    # =====计算资金曲线
    # 选取相关时间。币种上线10天之后的日期
    t = df.iloc[0]['candle_begin_time'] + timedelta(days=drop_days)
    df = df[df['candle_begin_time'] > t]

    # 计算资金曲线
    face_value = symbol_face_value[symbol.split('-')[0]]

    if if_kaka == 1:
        # 动态止损时第二个滑点设置为0
        df = equity_curve_for_OKEx_USDT_future_next_open_kaka(df, slippage=slippage, slippage2=0, c_rate=c_rate,
                                                              leverage_rate=leverage_rate,
                                                              face_value=face_value, min_margin_ratio=min_margin_ratio)

        # =====策略评价
        # 计算每笔交易
        trade = transfer_equity_curve_to_trade_kaka(df)

    elif if_kaka == 2:
        df = equity_curve_for_OKEx_USDT_future_next_open_kaka2(df, slippage=slippage, c_rate=c_rate,
                                                             leverage_rate=leverage_rate,
                                                             face_value=face_value, min_margin_ratio=min_margin_ratio)
        trade = transfer_equity_curve_to_trade_kaka(df)

    else:
        df = equity_curve_for_OKEx_USDT_future_next_open_ori(df, slippage=slippage, c_rate=c_rate,
                                                             leverage_rate=leverage_rate,
                                                             face_value=face_value, min_margin_ratio=min_margin_ratio)
        trade = transfer_equity_curve_to_trade(df)

    return trade